//
// Created by root on 19-7-29.
//

#include "inject_compacter.h"
#include "file_utils.h"
#include "log_utils.h"
#include "dex_utils.h"
#include "jni_utils.h"
#include "binder_utils.h"
#include "process_utils.h"
#include <dirent.h>
#include <unistd.h>

#define TAG "InjectCompacter"

#define COMPACTER_DEX_PATH "/data/local/tmp/SDCompacter.dex"
#define SD_ROOT_PATH "/data/system_debugger"
#define SD_ROOT_PATH_SYSTEM_SERVER "/data/system/system_debugger"

std::string make_zygote_opt_dir(){
    std::string dir_path=std::string("");
    if(get_process_name(getpid())=="zygote"){
        dir_path.append(SD_ROOT_PATH);
    }else{
        dir_path.append(SD_ROOT_PATH_SYSTEM_SERVER);
    }
    if(!file_exist(dir_path)) {
        std::string cmd_str=std::string("mkdir ");
        cmd_str.append(dir_path);
        const char* cmd=cmd_str.c_str();
        LOGD("make_zygote_opt_dir cmd:%s\n",cmd);
        system(cmd);
        if(file_exist(dir_path))
            return dir_path;
        return std::string("");
    }
    return dir_path;
}

void add_pb_sm_service(JNIEnv *env){
    jobject sys_classloader=get_sys_classloader(env);
    jclass pb_sm_class=load_by_specific_classloader(env,sys_classloader,
                                                    "com.pvdnc.systemdebugger.compacter.PBServiceManager");
    if(!pb_sm_class){
        LOGE("fail to find PBServiceManager in sys_classloader\n");
        return;
    }
    jmethodID pb_sm_constructor=env->GetMethodID(pb_sm_class,"<init>","()V");
    jobject pb_sm= env->NewObject(pb_sm_class,pb_sm_constructor);
    if(!pb_sm){
        LOGE("fail to construct PBServiceManager instance\n");
    }
    if(!add_service(env,"PBServiceManager",pb_sm)){
        LOGE("fail to add PBServiceManager service\n");
    }
}

void inject_compacter(JNIEnv *env) {
    std::string dex_path_str=std::string(COMPACTER_DEX_PATH);
    const char* dex_path=dex_path_str.c_str();
    if(!file_exist(dex_path_str)){
        LOGE("compacter_dex:%s is not found\n",dex_path);
        return;
    }
    const char* opt_dir=env->GetStringUTFChars(get_dir(env,"optDex"), nullptr);
    std::string opt_dir_str=std::string(opt_dir);
    if(opt_dir_str.empty()){
        LOGE("fail to create SD_ROOT_PATH:%s\n",opt_dir_str.c_str());
        return;
    }
    //const char* opt_dir=opt_dir_str.c_str();
    jobject dex_class_loader=new_dex_class_loader(env,dex_path,opt_dir,
            nullptr);
    patch_class_loader(env,get_sys_classloader(env),dex_class_loader);
    LOGD("compacter has been merged to sys_classloader\n");
    /*if(get_process_name(getpid())=="system_server")
        add_pb_sm_service(env);*/
    add_pb_sm_service(env);
}

bool post_auth_request(JNIEnv *env,std::string operation){
    jobject sys_classloader=get_sys_classloader(env);
    jclass service_manager_class=env->FindClass("android/os/ServiceManager");
    //public IBinder getService(String name)
    jmethodID get_service=env->GetStaticMethodID(service_manager_class,"getService",
            "(Ljava/lang/String;)Landroid/os/IBinder;");
    jobject remote=env->CallStaticObjectMethod(service_manager_class,get_service,
            env->NewStringUTF("AuthService"));
    if(!remote&&TryCatch(env)){//||或运算符只要第一个条件成立就不会管第二个条件
        LOGE("remote auth service has not been prepared yet\n");
        return false;//孤儿systemUI!!!
    }
    jclass auth_proxy_class=load_by_specific_classloader(env,sys_classloader,
            "com/pvdnc/systemdebugger/compacter/AuthProxy");
    jmethodID auth_proxy_constructor=env->GetMethodID(auth_proxy_class,"<init>","(Landroid/os/IBinder;)V");
    jobject auth_proxy=env->NewObject(auth_proxy_class,auth_proxy_constructor,remote);
    //public int request(String operation)
    jmethodID request=env->GetMethodID(auth_proxy_class,"request","(Ljava/lang/String;)I");
    jstring operation_=env->NewStringUTF(operation.c_str());

    int result_code=env->CallIntMethod(auth_proxy,
            request,operation_);
    LOGD("[JNI] auth_proxy get result:%d\n",result_code);

    return result_code==1;
}